#include "stdafx.h"
#include "BitmapFont.h"

#include "Log.h"
#include "Filesystem.h"
#include "MML.h"
#include "Timer.h"
#include "Engine.h"

namespace Cvekas {

BitmapFont::BitmapFont(SpriteBatchPtr sprite_batch, const std::string& filename, uint layer)
: sprite_batch(sprite_batch), layer(layer)
{
	LOG(FORMAT("Loading font file %s", % filename));

	Engine e;
	TextureManagerPtr texture_manager = e.getVideoDevice()->getTextureManager();

	memset(&desc, 0, sizeof(desc));

	TimeStamp t1 = Timer::getTimeStamp();
	MML font(Filesystem::readFileToString(filename));
	MMLNode& root = font.getRoot();
	TimeStamp t2 = Timer::getTimeStamp();

	int time = (t2-t1).toMiliSec();
	LOG(FORMAT("Parsed mml file in %d miliseconds", % time));
	
	if(root.name != "font")
		throw EXCEPTION("Root node must be named font");
	if(root.children.size() != 3)
		throw EXCEPTION("Root must have exactly 3 childs");

	desc.line_height = boost::lexical_cast<float>(root.getChild("height").value);

	// TODO: remove file extension from resource name
	std::string texture_filename = root.getChild("texture").value;
	desc.font_texture = texture_manager->loadTexture2D(texture_filename, Filesystem::redirect(texture_filename));

	MMLNode& chars = root.getChild("chars");
	std::list<MMLNode>::iterator itr = chars.children.begin();
	for(; itr != chars.children.end(); ++itr)
	{
		MMLNode& chr = *itr; 
		int id = boost::lexical_cast<int>(chr.value);
		assert(id > 0 && id < 256);

		desc.chars[id].source.x = boost::lexical_cast<float>(chr.getChild("x").value);
		desc.chars[id].source.y = boost::lexical_cast<float>(chr.getChild("y").value);
		desc.chars[id].source.width = boost::lexical_cast<float>(chr.getChild("w").value);
		desc.chars[id].source.height = boost::lexical_cast<float>(chr.getChild("h").value);
		desc.chars[id].x_offset = boost::lexical_cast<float>(chr.getChild("xoff").value);
		desc.chars[id].y_offset = boost::lexical_cast<float>(chr.getChild("yoff").value);
		desc.chars[id].x_advance = boost::lexical_cast<float>(chr.getChild("xadv").value);
	}
}

BitmapFont::BitmapFont(Cvekas::SpriteBatchPtr sprite_batch, const Cvekas::BitmapFontDesc &desc, uint layer)
: sprite_batch(sprite_batch), layer(layer)
{
	this->desc = desc;
}

BitmapFont::~BitmapFont()
{
}

void BitmapFont::draw(const Vector2& position, const std::string& text, const Color& color)
{
	Vector2 cursor = position;
	for(uint i = 0; i < text.size(); ++i)
	{
		if(byte(text[i]) < 32)
			continue;
		if(desc.chars[byte(text[i])].source.width == 0.0f || desc.chars[byte(text[i])].source.height == 0.0f)
		{
			cursor.x += desc.chars[byte(text[i])].x_advance;
			continue;
		}

		Vector2 pos = cursor;
		pos.x += desc.chars[byte(text[i])].x_offset;
		pos.y += desc.chars[byte(text[i])].y_offset;

		sprite_batch->draw(desc.font_texture, layer, desc.chars[byte(text[i])].source, pos, color);

		cursor.x += desc.chars[byte(text[i])].x_advance;
	}
}

void BitmapFont::drawScaled(const Vector2& center_position, float scale, const std::string& text, const Color& color)
{
	float width = getWidth(text) * scale;
	float height = desc.line_height * scale;

	Vector2 cursor = center_position;
	cursor.x -= width / 2.0f;
	cursor.y -= height / 2.0f;

	for(uint i = 0; i < text.size(); ++i)
	{
		if(byte(text[i]) < 32)
			continue;

		// Skip non-initialized chars
		if(desc.chars[byte(text[i])].source.width == 0.0f)
			continue;

		float pos_x = cursor.x + desc.chars[byte(text[i])].x_offset * scale;
		float pos_y = cursor.y + desc.chars[byte(text[i])].y_offset * scale;

		RectF dest(pos_x, pos_y, desc.chars[byte(text[i])].source.width * scale, desc.chars[byte(text[i])].source.height * scale);

		sprite_batch->draw(desc.font_texture, layer, desc.chars[byte(text[i])].source, dest, color);

		cursor.x += desc.chars[byte(text[i])].x_advance * scale;
	}
}

void BitmapFont::drawToRect(const RectF& rect, const std::string& text, const Color& color)
{
	Vector2 cursor(rect.x, rect.y);

	uint i = 0;
	while(i < text.size() && cursor.y < rect.y + rect.height)
	{
		// Get next word
		std::string word = "";
		while(i < text.size() && text[i] != ' ' && text[i] != '\n' && text[i] != '\t')
		{
			word += text[i++];
		}
		if(i < text.size())
			word += text[i++];

		float word_width = getWidth(word);

		// If word does not fit into current line...
		if(cursor.x + word_width > rect.x + rect.width)
		{
			// Begin new line
			cursor.y += desc.line_height;
			cursor.x = rect.x;
		}
		
		// Draw word
		for(uint j = 0; j < word.size(); ++j)
		{
			if(byte(word[j]) < 32)
				continue;
			
			// Skip non-initialized chars
			if(desc.chars[byte(word[j])].source.width == 0.0f)
				continue;

			Vector2 pos = cursor;
			pos.x += desc.chars[byte(word[j])].x_offset;
			pos.y += desc.chars[byte(word[j])].y_offset;

			sprite_batch->draw(desc.font_texture, layer, desc.chars[byte(word[j])].source, pos, color);

			cursor.x += desc.chars[byte(word[j])].x_advance;
		}

		// Begin new line if word ends with '\n'
		if(word[word.size()-1] == '\n')
		{
			cursor.y += desc.line_height;
			cursor.x = rect.x;
		}
	}
}

float BitmapFont::getWidth(const std::string& text) const
{
	float width = 0.0f;

	for(uint i = 0; i < text.size(); ++i)
	{
		if(byte(text[i]) < 32)
			continue;
		// Skip non-initialized chars
		if(desc.chars[byte(text[i])].source.width == 0.0f)
			continue;

		width += desc.chars[byte(text[i])].x_advance;
	}

	return width;
}

} // namespace